﻿#region Disclaimer/Info

/////////////////////////////////////////////////////////////////////////////////////////////////
//
//   File:		RazorViewEngine.cs
//   Website:		http://dexterblogengine.com/
//   Authors:		http://dexterblogengine.com/About.ashx
//   Rev:		1
//   Created:		19/01/2011
//   Last edit:		19/01/2011
//   License:		GNU Library General Public License (LGPL)
// 
//   For updated news and information please visit http://dexterblogengine.com/
//   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
//   For any question contact info@dexterblogengine.com
//
///////////////////////////////////////////////////////////////////////////////////////////////////

#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Dexter.Services;

namespace Dexter.Web.Mvc.ViewEngines {
	public class RazorViewEngine : System.Web.Mvc.RazorViewEngine {
		/// <summary>
		/// 	Initializes a new instance of the <see cref = "RazorViewEngine" /> class.
		/// </summary>
		/// <param name = "configuration">The configuration.</param>
		public RazorViewEngine ( IConfigurationService configuration )
			: this ( ) {
			this.Configuration = configuration;
		}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "RazorViewEngine" /> class.
		/// </summary>
		public RazorViewEngine ( ) {
			base.MasterLocationFormats = new[] {"~/Themes/{2}/Views/{1}/{0}.cshtml" , "~/Themes/{2}/Views/{1}/{0}.vbhtml" , "~/Themes/{2}/Views/Shared/{0}.cshtml" , "~/Themes/{2}/Views/Shared/{0}.vbhtml"};
			base.ViewLocationFormats = new[] {"~/Themes/{2}/Views/{1}/{0}.cshtml" , "~/Themes/{2}/Views/{1}/{0}.vbhtml" , "~/Themes/{2}/Views/Shared/{0}.cshtml" , "~/Themes/{2}/Views/Shared/{0}.vbhtml"};
			base.PartialViewLocationFormats = new[] {"~/Themes/{2}/Views/{1}/{0}.cshtml" , "~/Themes/{2}/Views/{1}/{0}.vbhtml" , "~/Themes/{2}/Views/Shared/{0}.cshtml" , "~/Themes/{2}/Views/Shared/{0}.vbhtml"};

			base.AreaViewLocationFormats = new string[] {"~/Areas/{2}/Views/{1}/{0}.cshtml" , "~/Areas/{2}/Views/{1}/{0}.vbhtml" , "~/Areas/{2}/Views/Shared/{0}.cshtml" , "~/Areas/{2}/Views/Shared/{0}.vbhtml"};
			base.AreaMasterLocationFormats = new string[] {"~/Areas/{2}/Views/{1}/{0}.cshtml" , "~/Areas/{2}/Views/{1}/{0}.vbhtml" , "~/Areas/{2}/Views/Shared/{0}.cshtml" , "~/Areas/{2}/Views/Shared/{0}.vbhtml"};
			base.AreaPartialViewLocationFormats = new string[] {"~/Areas/{2}/Views/{1}/{0}.cshtml" , "~/Areas/{2}/Views/{1}/{0}.vbhtml" , "~/Areas/{2}/Views/Shared/{0}.cshtml" , "~/Areas/{2}/Views/Shared/{0}.vbhtml"};
		}

		IConfigurationService Configuration { get; set; }

		/// <summary>
		/// 	Gets a value that indicates whether a file exists in the specified virtual file system (path).
		/// </summary>
		/// <param name = "controllerContext">The controller context.</param>
		/// <param name = "virtualPath">The virtual path.</param>
		/// <returns>
		/// 	true if the file exists in the virtual file system; otherwise, false.
		/// </returns>
		protected override bool FileExists ( ControllerContext controllerContext , string virtualPath ) {
			try {
				return File.Exists ( controllerContext.HttpContext.Server.MapPath ( virtualPath ) );
			}
			catch ( HttpException exception ) {
				if ( exception.GetHttpCode ( ) != 404 ) {
					throw;
				}

				return false;
			}
			catch {
				return false;
			}
		}

		/// <summary>
		/// 	Finds the specified view by using the specified controller context and master view name.
		/// </summary>
		/// <param name = "controllerContext">The controller context.</param>
		/// <param name = "viewName">The name of the view.</param>
		/// <param name = "masterName">The name of the master view.</param>
		/// <param name = "useCache">true to use the cached view.</param>
		/// <returns>The page view.</returns>
		/// <exception cref = "T:System.ArgumentNullException">The <paramref name = "controllerContext" /> parameter is null (Nothing in Visual Basic).</exception>
		/// <exception cref = "T:System.ArgumentException">The <paramref name = "viewName" /> parameter is null or empty.</exception>
		public override ViewEngineResult FindView ( ControllerContext controllerContext , string viewName , string masterName , bool useCache ) {
			string[] strArray;
			string[] strArray2;

			if (controllerContext == null)
				throw new ArgumentNullException ( "controllerContext" );

			if (viewName.IsNullOrEmpty ( ))
				throw new ArgumentException ( );

			string themeName = Configuration.Configuration.ThemeName;

			string requiredString = controllerContext.RouteData.GetRequiredString ( "controller" );

			string viewPath = this.GetPath ( controllerContext , this.ViewLocationFormats , this.AreaViewLocationFormats , viewName , requiredString , "View" , useCache , out strArray , themeName );
			string masterPath = this.GetPath ( controllerContext , this.MasterLocationFormats , this.AreaMasterLocationFormats , masterName , requiredString , "Master" , useCache , out strArray2 , themeName );

			if ( !string.IsNullOrEmpty ( viewPath ) && ( !string.IsNullOrEmpty ( masterPath ) || string.IsNullOrEmpty ( masterName ) ) ) {
				return new ViewEngineResult ( CreateView ( controllerContext , viewPath , masterPath ) , this );
			}

			ViewEngineResult view = new ViewEngineResult ( strArray.Union ( strArray2 ) );

			return view;
		}

		/// <summary>
		/// 	Finds the specified partial view by using the specified controller context.
		/// </summary>
		/// <param name = "controllerContext">The controller context.</param>
		/// <param name = "partialViewName">The name of the partial view.</param>
		/// <param name = "useCache">true to use the cached partial view.</param>
		/// <returns>The partial view.</returns>
		/// <exception cref = "T:System.ArgumentNullException">The <paramref name = "controllerContext" /> parameter is null (Nothing in Visual Basic).</exception>
		/// <exception cref = "T:System.ArgumentException">The <paramref name = "partialViewName" /> parameter is null or empty.</exception>
		public override ViewEngineResult FindPartialView ( ControllerContext controllerContext , string partialViewName , bool useCache ) {
			string[] strArray;
			
			if (controllerContext == null)
				throw new ArgumentNullException ( "controllerContext" );

			if (partialViewName.IsNullOrEmpty (  ))
				throw new ArgumentException ( "partialViewName" );

			string themeName = Configuration.Configuration.ThemeName;

			string requiredString = controllerContext.RouteData.GetRequiredString ( "controller" );

			string partialViewPath = this.GetPath ( controllerContext , this.PartialViewLocationFormats , this.AreaPartialViewLocationFormats , partialViewName , requiredString , "Partial" , useCache , out strArray , themeName );

			if ( string.IsNullOrEmpty ( partialViewPath ) ) {
				return new ViewEngineResult ( strArray );
			}

			return new ViewEngineResult ( CreatePartialView ( controllerContext , partialViewPath ) , this );
		}

		string GetPath ( ControllerContext controllerContext , IEnumerable <string> locations , string[] areaLocations , string name , string controllerName , string cacheKeyPrefix , bool useCache , out string[] searchedLocations , string themeName ) {
			searchedLocations = new string[0];

			if ( string.IsNullOrEmpty ( name ) ) {
				return string.Empty;
			}

			string areaName = AreaHelper.GetAreaName ( controllerContext.RouteData );
			bool flag = !string.IsNullOrEmpty ( areaName );

			List <ViewLocation> viewLocations = GetViewLocations ( locations , flag
			                                                                   	? areaLocations
			                                                                   	: null );

			if ( viewLocations.Count == 0 ) {
				throw new InvalidOperationException ( "locations must not be null or emtpy." );
			}

			bool flag2 = IsSpecificPath ( name );
			string key = this.CreateCacheKey ( cacheKeyPrefix , name , flag2
			                                                           	? string.Empty
			                                                           	: controllerName , areaName , themeName );

			if ( useCache ) {
				string viewLocation = ViewLocationCache.GetViewLocation ( controllerContext.HttpContext , key );

				if ( viewLocation != null ) {
					return viewLocation;
				}
			}

			return !flag2
			       	? this.GetPathFromGeneralName ( controllerContext , viewLocations , name , controllerName , areaName , key , ref searchedLocations , themeName )
			       	: this.GetPathFromSpecificName ( controllerContext , name , key , ref searchedLocations );
		}

		static bool IsSpecificPath ( string name ) {
			char ch = name[ 0 ];
			if ( ch != '~' ) {
				return ( ch == '/' );
			}
			return true;
		}

		string CreateCacheKey ( string prefix , string name , string controllerName , string areaName , string themeName ) {
			return string.Format ( CultureInfo.InvariantCulture , ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:{5}:" , new object[] {GetType ( ).AssemblyQualifiedName , prefix , name , controllerName , areaName ?? "nullArea" , themeName} );
		}

		static List <ViewLocation> GetViewLocations ( IEnumerable <string> viewLocationFormats , IEnumerable <string> areaViewLocationFormats ) {
			List <ViewLocation> list = new List <ViewLocation> ( );
			if ( areaViewLocationFormats != null ) {
				list.AddRange ( areaViewLocationFormats.Select ( str => new AreaAwareViewLocation ( str ) ) );
			}

			if ( viewLocationFormats != null ) {
				list.AddRange ( viewLocationFormats.Select ( str2 => new ViewLocation ( str2 ) ) );
			}

			return list;
		}

		string GetPathFromGeneralName ( ControllerContext controllerContext , List <ViewLocation> locations , string name , string controllerName , string areaName , string cacheKey , ref string[] searchedLocations , string themeName ) {
			string virtualPath = string.Empty;
			searchedLocations = new string[locations.Count];
			for ( int i = 0; i < locations.Count; i++ ) {
				string str2 = locations[ i ].Format ( name , controllerName , areaName , themeName );
				if ( this.FileExists ( controllerContext , str2 ) ) {
					searchedLocations = new string[0];
					virtualPath = str2;
					this.ViewLocationCache.InsertViewLocation ( controllerContext.HttpContext , cacheKey , virtualPath );
					return virtualPath;
				}
				searchedLocations[ i ] = str2;
			}
			return virtualPath;
		}

		string GetPathFromSpecificName ( ControllerContext controllerContext , string name , string cacheKey , ref string[] searchedLocations ) {
			string virtualPath = name;
			if ( !this.FileExists ( controllerContext , name ) ) {
				virtualPath = string.Empty;
				searchedLocations = new[] {name};
			}

			this.ViewLocationCache.InsertViewLocation ( controllerContext.HttpContext , cacheKey , virtualPath );
			return virtualPath;
		}
	}
}